Police Brutatlity - Data Exploration

Contributor(s): Jessica Marx

Date: 08 June 2020

Code: GitHub

Data: Sourced from this Google Sheet and this census data from Kaggle

The following is not meant to be a source of truth in terms of data integrity, but rather to illustrate different ways of displaying, engineering, and/or presenting the data.

# load packages, install if needed
packages = c(
      "dplyr"
    , "ggplot2"
    , "formattable"
    , "plotly"
    , "RColorBrewer"
    , "scales"
    , "stringr"
    , "tidyr"
    , "ElmeR"
    , "RJDBC"
    , "kableExtra"
    , "wesanderson"
    , "reshape2"
    , "rtweet"
    , "tidytext"
    , "lubridate"
    , "wordcloud"
    , "ggpubr"
    , "ggthemes"
    , "knitrBootstrap"
    , "DT"
    , "MatchIt"
    , "beyonce"
    , "UpSetR"
    , "gganimate"
    , "wordcloud2"
    , "widyr"
    , "ggraph"
    , "igraph"
    , "aod"
    , "corrplot"
    , "ROCR"
    , "InformationValue"
    , "car"
    , "glmnet"
    , "caret"
    , "kernlab"
    , "pdp"
    , "rpart.plot"
    , "rpart"
    , "e1071"
    )
package.check <- lapply(packages, FUN = function(x) {
  if (!require(x, character.only = TRUE)) {
    install.packages(x, dependencies = TRUE)
    library(x, character.only = TRUE)
  }
})
#import the data
library(readr)
us_county_stats <- read_csv("datasets_579969_1220276_us_county.csv")
brutality_cases <- read_csv("police_brutality.csv")
# glimpse the data
# us_county_stats %>% 
#   head() 
# 
# brutality_cases %>% 
#   head()

Columns and percent of rows with missing fields from Brutality dataset.

#missing values
colMeans(is.na(brutality_cases))
                                          Timestamp                        Date of the Police Brutality 
                                       1.0000000000                                        0.0000000000 
           Approximate Time of the Police Brutality           State where the Police Brutality Occurred 
                                       1.0000000000                                        0.0000000000 
           City where the Police Brutality Occurred                   Video URL of the Police Brutality 
                                       0.0007829832                                        0.4519117839 
                          Names of Victims Involved                   Names of Police Officers Involved 
                                       0.0000000000                                        1.0000000000 
          Badge Numbers of Police Officers involved Enter any extra information you want to share here. 
                                       1.0000000000                                        1.0000000000 
                                           Category                                                test 
                                       1.0000000000                                        1.0000000000 
                                             County                                          Category_1 
                                       0.0000000000                                        1.0000000000 

Summary of US census dataset.

summary(us_county_stats)
      fips          county             state            state_code             male             female          median_age   
 Min.   : 1001   Length:3220        Length:3220        Length:3220        Min.   :     38   Min.   :     37   Min.   :21.70  
 1st Qu.:19032   Class :character   Class :character   Class :character   1st Qu.:   5658   1st Qu.:   5573   1st Qu.:38.10  
 Median :30024   Mode  :character   Mode  :character   Mode  :character   Median :  12916   Median :  12996   Median :41.20  
 Mean   :31394                                                            Mean   :  49875   Mean   :  51457   Mean   :41.28  
 3rd Qu.:46106                                                            3rd Qu.:  33248   3rd Qu.:  33531   3rd Qu.:44.30  
 Max.   :72153                                                            Max.   :4976788   Max.   :5121264   Max.   :67.00  
   population       female_percentage      lat             long        
 Min.   :      75   Min.   :21.00     Min.   :17.98   Min.   :-164.03  
 1st Qu.:   11214   1st Qu.:49.43     1st Qu.:34.35   1st Qu.: -98.09  
 Median :   25950   Median :50.42     Median :38.21   Median : -89.95  
 Mean   :  101332   Mean   :49.96     Mean   :37.97   Mean   : -91.64  
 3rd Qu.:   66552   3rd Qu.:51.15     3rd Qu.:41.69   3rd Qu.: -82.99  
 Max.   :10098052   Max.   :58.61     Max.   :69.31   Max.   : -65.29  
# Clean dataset (drop nulls)
brutality_clean = brutality_cases
# Remove columns with more than 50% NA
brutality_clean = brutality_clean[, which(colMeans(!is.na(brutality_clean)) > 0.5)]
# Change columns to snake case
snake_case <- function(x) {
  colnames(x) <- gsub(" ", "_", colnames(x));x
  colnames(x) <-tolower(colnames(x))
}
colnames(brutality_clean) = snake_case(brutality_clean)
brutality_clean = brutality_clean %>% 
  rename(
   "state" = state_where_the_police_brutality_occurred
   , "city" = city_where_the_police_brutality_occurred
   , "date" = date_of_the_police_brutality
  ) %>% 
  mutate(
    state_county = paste0(state, "-", county)
  )
brutality_clean$date = as.Date(brutality_clean$date, "%m/%d/%Y")
us_stats_clean = us_county_stats
us_stats_clean$county = str_replace(us_stats_clean$county, " County", "")
us_stats_clean = us_stats_clean %>% 
  group_by(state) %>% 
  mutate(state_pop = sum(population)) %>% 
  rename(
    "state_full" = state
    , "state" = state_code
    ) %>% 
  ungroup()
# us_stats_clean = 
  
us_stats_clean = us_stats_clean %>% 
  mutate(state_county = paste0(state, "-", county)) %>% 
  select(state_pop, state) %>% 
  unique()
df_merged = brutality_clean %>% 
  left_join(us_stats_clean) %>% 
  mutate(state_pop = ifelse(state == "DC", 705749, state_pop)
  ) %>% 
  unique()
df_merged = df_merged %>% 
  group_by(
    state
  ) %>% 
  add_tally() %>% 
  ungroup() %>% 
  rename(
    "state_totals" = n
  ) %>% 
  mutate(
    state_pop_millions = state_pop/1000000
    , totals_per_million = state_totals/state_pop_millions
  ) 

Incidents by State

df_plot = df_merged %>% 
  select(
    state, state_pop, state_pop_millions, state_totals, totals_per_million
  ) %>% 
  unique() %>% 
  arrange(desc(totals_per_million)) %>% 
  mutate(
    state = reorder(as.factor(state), totals_per_million)
  ) 
a = df_plot %>% 
  plot_ly(
    y = ~state
    , x = ~totals_per_million
    , type = "bar"
    , color = paired_better[1]
    , orientation = "h"
    , width = 900
    , height = 1000
    , hoverinfo = "text"
    , text = ~paste(
      "State: ", state
      , "<br>State Population: ", comma(state_pop)
      , "<br>Reported Incidents: ", state_totals
      , "<br>Incidents per Million Residents: ", comma(totals_per_million)
    )
  ) %>% 
  layout(
    xaxis = list(title = "Reported Incidents per Million State Residents")
    , yaxis = list(title = "")
  )
b = df_plot %>% 
  plot_ly(
    y = ~state
    , x = ~state_totals
    , type = "bar"
    , color = paired_better[10]
    , orientation = "h"
    , width = 900
    , height = 1000
    , hoverinfo = "text"
    , showlegend = FALSE
    , text = ~paste(
      "State: ", state
      , "<br>State Population: ", comma(state_pop)
      , "<br>Reported Incidents: ", state_totals
      , "<br>Incidents per Million Residents: ", comma(totals_per_million)
    )
  ) %>% 
  layout(
    xaxis = list(title = "Total Reported Incidents")
    , yaxis = list(title = "")
  )
subplot(a, b, titleX = TRUE)

Excluding counties with null populations

There are some counties with null values when it comes to population. We can obviously find these via a better dataset, but for now I’m going to exclude them in order to provide examples with the data that we have. Obviously, this is not accurate reporting.

County Incidents per Capita

df_filtered = df_merged
df_filtered = df_filtered %>% 
  left_join(us_stats_clean) %>% 
  filter(!is.na(population)) 
df_filtered = df_filtered %>% 
  group_by(state, county) %>% 
  add_tally() %>% 
  rename("county_totals" = n) %>% 
  arrange(desc(county_totals)) %>% 
  mutate("incidents_per_capita" = county_totals/(population)) %>% 
  ungroup() 
df_county = df_filtered %>% 
  select(state, state_county, county, state_full, state_totals, median_age, population, county_totals, incidents_per_capita) %>% 
  unique() %>% 
  arrange(desc(incidents_per_capita))
df_county %>% 
  mutate(
    incidents_per_capita = comma(incidents_per_capita, .000001)
    , population = comma(population)
    ) %>% 
  datatable()
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IGZhbHNlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgCi0tLQoKPCEtLSBDU1MgLS0+Cgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgp0aCB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjNDQ0OTREOwogICAgY29sb3I6IHdoaXRlOwogICAgZm9udC1zaXplOiAxMHB0OwogICAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgPCEtLSBtYXJnaW4tbGVmdDogYXV0bzsgLS0+CiAgICA8IS0tIG1hcmdpbi1yaWdodDogYXV0bzsgLS0+CiAgICA8IS0tIHBhZGRpbmctdG9wOiAyNXB4OyAtLT4KICB9Cgp0ZCB7ICAvKiBUYWJsZSAgKi8KICBmb250LXNpemU6IDEwcHQ7CiAgPCEtLSB0ZXh0LWFsaWduOiBjZW50ZXI7IC0tPgogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwogIDwhLS0gcGFkZGluZy10b3A6IDI1cHg7IC0tPgp9CmEgewogIGNvbG9yOiAjMWY3OGI0OwogIGZvbnQtc2l6ZTogMTJwdDsKICBmb250LWZhbWlseTogIkFyaWFsOwp9CmJvZHkgewogIGZvbnQtc2l6ZTogMTJwdDsKICBmb250LWZhbWlseTogIkFyaWFsIiwgc2Fucy1zZXJpZjsKfQoKaDEgewogIGZvbnQtc2l6ZTogMTRwdDsKICBzZm9udC1zdHlsZTogaXRhbGljOwogIGZvbnQtd2VpZ2h0OiBib2xkZXI7CiAgY29sb3I6ICM0NDQ5NEQ7CiAgY29sb3I6ICM0NDQ5NEQ7CiAgPCEtLSBmb250LXdlaWdodDogYm9sZGVyOyAtLT4KICAKfQpoMiB7CiAgZm9udC1zaXplOiAxMnB0OwogIDwhLS0gZm9udC1zdHlsZTogaXRhbGljOyAtLT4KICBmb250LXdlaWdodDogYm9sZGVyOwogIGNvbG9yOiAjNDQ0OTREOwogIH0KICAKaDMgewogIGZvbnQtc2l6ZTogMTJwdDsKICA8IS0tIGZvbnQtd2VpZ2h0OiBib2xkZXI7IC0tPgogIGNvbG9yOiAjNUVCQUNFOwp9CgpoNCB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtd2VpZ2h0OiBib2xkZXI7CiAgY29sb3I6ID0jNDQ0OTREOwp9Cgouc2lkZW5hdiB7CiAgaGVpZ2h0OiAxMDAlOwogIHdpZHRoOiAyMDBweDsKICBwb3NpdGlvbjogZml4ZWQ7CiAgei1pbmRleDogMTsKICB0b3A6IDA7CiAgbGVmdDogMDsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjYTZjZWUzOwogIG92ZXJmbG93LXg6IGhpZGRlbjsKICBwYWRkaW5nLXRvcDogMjBweDsKfQoKLnNpZGVuYXYgYSB7CiAgcGFkZGluZzogNnB4IDhweCA2cHggMTZweDsKICB0ZXh0LWRlY29yYXRpb246IG5vbmU7CiAgZm9udC1zaXplOiAxMnB0OwogIDwhLS0gZm9udC13ZWlnaHQ6IGJvbGRlcjsgLS0+CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgY29sb3I6ICNGRkZGRkY7CiAgZGlzcGxheTogYmxvY2s7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgouY2VudGVyIHsKICBkaXNwbGF5OiBibG9jazsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87CiAgd2lkdGg6IDEwMCU7Cn0KCi5zaWRlbmF2IGE6aG92ZXIgewogIGNvbG9yOiAjZjFmMWYxOwp9CgoubWFpbiB7CiAgbWFyZ2luLWxlZnQ6IDIwMHB4OyAvKiBTYW1lIGFzIHRoZSB3aWR0aCBvZiB0aGUgc2lkZW5hdiAqLwoKfQoubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTQwMHB4OwogIG1hcmdpbi1sZWZ0OiBhdXRvOwogIG1hcmdpbi1yaWdodDogYXV0bzsKICBwYWRkaW5nOiAyNXB4Cn0KICAvKnBhZGRpbmc6IDBweCA1cHg7ICovCn0KCkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtaGVpZ2h0OiA0NTBweCkgewogIC5zaWRlbmF2IHtwYWRkaW5nLXRvcDogMTVweDt9CiAgLnNpZGVuYXYgYSB7Zm9udC1zaXplOiAxOHB4O30KfQo8L3N0eWxlPgoKPCEtLSBUSVRMRSBJTkZPICAtLT4KCiMgUG9saWNlIEJydXRhdGxpdHkgLSBEYXRhIEV4cGxvcmF0aW9uCiMjIENvbnRyaWJ1dG9yKHMpOiBKZXNzaWNhIE1hcngKIyMgRGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICIlZCAlQiAlWSIpYAojIyBDb2RlOiA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vbXphZ2Fpbm92YS9wYi1kYXNoYm9hcmQiIHRhcmdldD0iX2JsYW5rIj5HaXRIdWI8L2E+CiMjIERhdGE6IFNvdXJjZWQgZnJvbSB0aGlzIDxhIGhyZWY9Imh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzExd0xXTTk1N3dreFJJeUs3RUZXLVNvOWpxR2h2UXhjWjgzaXBtMWtWRW5RL2VkaXQjZ2lkPTkxNjQxMTgwNiIgdGFyZ2V0PSJfYmxhbmsiPkdvb2dsZSBTaGVldDwvYT4gYW5kIHRoaXMgY2Vuc3VzIGRhdGEgZnJvbSA8YSBocmVmPSJodHRwczovL3d3dy5rYWdnbGUuY29tL2hlYWRzb3J0YWlscy9jb3ZpZDE5LXVzLWNvdW50eS1qaHUtZGF0YS1kZW1vZ3JhcGhpY3M/c2VsZWN0PXVzX2NvdW50eS5jc3YiIHRhcmdldD0iX2JsYW5rIj5LYWdnbGU8L2E+CgpUaGUgZm9sbG93aW5nIGlzIG5vdCBtZWFudCB0byBiZSBhIHNvdXJjZSBvZiB0cnV0aCBpbiB0ZXJtcyBvZiBkYXRhIGludGVncml0eSwgYnV0IHJhdGhlciB0byBpbGx1c3RyYXRlIGRpZmZlcmVudCB3YXlzIG9mIGRpc3BsYXlpbmcsIGVuZ2luZWVyaW5nLCBhbmQvb3IgcHJlc2VudGluZyB0aGUgZGF0YS4gCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpCgpgYGAKCgpgYGB7ciBwYWNrYWdlLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBsb2FkIHBhY2thZ2VzLCBpbnN0YWxsIGlmIG5lZWRlZApwYWNrYWdlcyA9IGMoCiAgICAgICJkcGx5ciIKICAgICwgImdncGxvdDIiCiAgICAsICJmb3JtYXR0YWJsZSIKICAgICwgInBsb3RseSIKICAgICwgIlJDb2xvckJyZXdlciIKICAgICwgInNjYWxlcyIKICAgICwgInN0cmluZ3IiCiAgICAsICJ0aWR5ciIKICAgICwgIkVsbWVSIgogICAgLCAiUkpEQkMiCiAgICAsICJrYWJsZUV4dHJhIgogICAgLCAid2VzYW5kZXJzb24iCiAgICAsICJyZXNoYXBlMiIKICAgICwgInJ0d2VldCIKICAgICwgInRpZHl0ZXh0IgogICAgLCAibHVicmlkYXRlIgogICAgLCAid29yZGNsb3VkIgogICAgLCAiZ2dwdWJyIgogICAgLCAiZ2d0aGVtZXMiCiAgICAsICJrbml0ckJvb3RzdHJhcCIKICAgICwgIkRUIgogICAgLCAiTWF0Y2hJdCIKICAgICwgImJleW9uY2UiCiAgICAsICJVcFNldFIiCiAgICAsICJnZ2FuaW1hdGUiCiAgICAsICJ3b3JkY2xvdWQyIgogICAgLCAid2lkeXIiCiAgICAsICJnZ3JhcGgiCiAgICAsICJpZ3JhcGgiCiAgICAsICJhb2QiCiAgICAsICJjb3JycGxvdCIKICAgICwgIlJPQ1IiCiAgICAsICJJbmZvcm1hdGlvblZhbHVlIgogICAgLCAiY2FyIgogICAgLCAiZ2xtbmV0IgogICAgLCAiY2FyZXQiCiAgICAsICJrZXJubGFiIgogICAgLCAicGRwIgogICAgLCAicnBhcnQucGxvdCIKICAgICwgInJwYXJ0IgogICAgLCAiZTEwNzEiCiAgICApCgpwYWNrYWdlLmNoZWNrIDwtIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgewogIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKICB9Cn0pCmBgYAoKCmBgYHtyIGZ1bmN0aW9ucywgZWNobz1GQUxTRX0KCiNmdW5jdGlvbnMhCgojcm91bmQgCnJvdW5kLnRvIDwtIGZ1bmN0aW9uKHgsIGIpIHsKICByb3VuZCh4L2IpKmIKfQoKI29kZHMgdG8gcHJvYmFiaWxpdHkKb2Rkcy50by5wcm9iIDwtIGZ1bmN0aW9uKG9kZHMpIHsKICBvZGRzLygxICsgb2RkcykgCn0KCiNsb2cgb2RkcyB0byBwcm9iYWJpbGl0eSAKbG9naXQycHJvYiA8LSBmdW5jdGlvbihsb2dpdCl7CiAgb2RkcyA8LSBleHAobG9naXQpCiAgcHJvYiA8LSBvZGRzIC8gKDEgKyBvZGRzKQogIHJldHVybihwcm9iKQp9CgojY29udmVydCB0byBhIHJhbmdlCnJhbmdlMDEgPC0gZnVuY3Rpb24oeCl7CiAgKHgtbWluKHgpKS8obWF4KHgpLW1pbih4KSkKfQoKI2Z1bmN0aW9uIHRvIGdldCB2ZWN0b3Igb2YgY29sb3IgdmFsdWVzIGZyb20gUkNvbG9yQnJld2VyCmdldF9oZXhfdmFsdWVzIDwtIGZ1bmN0aW9uKHBhbCkgewogIGJyZXdlci5wYWwoYnJld2VyLnBhbC5pbmZvW3BhbCwgIm1heGNvbG9ycyJdLCBwYWwpCn0KcGFpcmVkX2NvbHMgPC0gZ2V0X2hleF92YWx1ZXMocGFsID0gIlBhaXJlZCIpCgojcGFpcmVkIHBhbGV0dGUgd2l0aCBicmlnaHRlciB5ZWxsb3cgKHVzZSB0aGlzIGZvciBkaXZpc2lvbmFsIGNvbG9yIG1hcHBpbmcpCnBhaXJlZF9iZXR0ZXIgPC0gcmVwbGFjZShwYWlyZWRfY29scywgcGFpcmVkX2NvbHMgPT0gIiNGRkZGOTkiLCAiI2ZlZDk3NiIpCgojcm91bmQgdG8gbmVhcmVzdCA1LCAxMCwgZXRjIApyb3VuZF9uZWFyZXN0ID0gZnVuY3Rpb24oeCwgYmFzZSkgewogIGJhc2Uqcm91bmQoeC9iYXNlKQp9CgpsZWZ0ID0gZnVuY3Rpb24odGV4dCwgbnVtX2NoYXIpIHsKICBzdWJzdHIodGV4dCwgMSwgbnVtX2NoYXIpCn0KCm1pZCA9IGZ1bmN0aW9uKHRleHQsIHN0YXJ0X251bSwgbnVtX2NoYXIpIHsKICBzdWJzdHIodGV4dCwgc3RhcnRfbnVtLCBzdGFydF9udW0gKyBudW1fY2hhciAtIDEpCn0KCnJpZ2h0ID0gZnVuY3Rpb24odGV4dCwgbnVtX2NoYXIpIHsKICBzdWJzdHIodGV4dCwgbmNoYXIodGV4dCkgLSAobnVtX2NoYXItMSksIG5jaGFyKHRleHQpKQp9CgojdHVybiB2YWx1ZXMgb3V0c2lkZSBvZiBJUVIgdG8gTkEKb3V0bGllcnJlcGxhY2VtZW50IDwtIGZ1bmN0aW9uKGRhdGFmcmFtZSl7CiAgZGF0YWZyYW1lICU+JSAgICAgICAgICAKICAgICAgICAgICBtYXBfaWYoaXMubnVtZXJpYywgfiByZXBsYWNlKC54LCAueCAlaW4lIGJveHBsb3Quc3RhdHMoLngpJG91dCwgTkEpKSAlPiUKICAgICAgICAgICBkcGx5cjo6YmluZF9jb2xzKCkgCn0KCiNldmFsdWF0ZSBtb2RlbApldmFsX21ldHJpY3MgPSBmdW5jdGlvbihtb2RlbCwgZGYsIHByZWRpY3Rpb25zLCB0YXJnZXQpewogIHJlc2lkcyA9IGRmWyx0YXJnZXRdIC0gcHJlZGljdGlvbnMKICByZXNpZHMyID0gcmVzaWRzKioyCiAgTiA9IGxlbmd0aChwcmVkaWN0aW9ucykKICByMiA9IGFzLmNoYXJhY3Rlcihyb3VuZChzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQsIDIpKQogIGFkal9yMiA9IGFzLmNoYXJhY3Rlcihyb3VuZChzdW1tYXJ5KG1vZGVsKSRhZGouci5zcXVhcmVkLCAyKSkKICBwcmludChhZGpfcjIpICNBZGp1c3RlZCBSLXNxdWFyZWQKICBwcmludChhcy5jaGFyYWN0ZXIocm91bmQoc3FydChzdW0ocmVzaWRzMikvTiksIDIpKSkgI1JNU0UKfQoKYGBgCgpgYGB7cn0KCiNpbXBvcnQgdGhlIGRhdGEKbGlicmFyeShyZWFkcikKdXNfY291bnR5X3N0YXRzIDwtIHJlYWRfY3N2KCJkYXRhc2V0c181Nzk5NjlfMTIyMDI3Nl91c19jb3VudHkuY3N2IikKYnJ1dGFsaXR5X2Nhc2VzIDwtIHJlYWRfY3N2KCJwb2xpY2VfYnJ1dGFsaXR5LmNzdiIpCgpgYGAKCgpgYGB7cn0KCiMgZ2xpbXBzZSB0aGUgZGF0YQojIHVzX2NvdW50eV9zdGF0cyAlPiUgCiMgICBoZWFkKCkgCiMgCiMgYnJ1dGFsaXR5X2Nhc2VzICU+JSAKIyAgIGhlYWQoKQoKYGBgCgojIyBDb2x1bW5zIGFuZCBwZXJjZW50IG9mIHJvd3Mgd2l0aCBtaXNzaW5nIGZpZWxkcyBmcm9tIEJydXRhbGl0eSBkYXRhc2V0LiAKYGBge3J9CgojbWlzc2luZyB2YWx1ZXMKY29sTWVhbnMoaXMubmEoYnJ1dGFsaXR5X2Nhc2VzKSkKCmBgYAoKIyMgU3VtbWFyeSBvZiBVUyBjZW5zdXMgZGF0YXNldC4gCgpgYGB7cn0KCnN1bW1hcnkodXNfY291bnR5X3N0YXRzKQoKYGBgCgpgYGB7cn0KCiMgQ2xlYW4gZGF0YXNldCAoZHJvcCBudWxscykKYnJ1dGFsaXR5X2NsZWFuID0gYnJ1dGFsaXR5X2Nhc2VzCgojIFJlbW92ZSBjb2x1bW5zIHdpdGggbW9yZSB0aGFuIDUwJSBOQQpicnV0YWxpdHlfY2xlYW4gPSBicnV0YWxpdHlfY2xlYW5bLCB3aGljaChjb2xNZWFucyghaXMubmEoYnJ1dGFsaXR5X2NsZWFuKSkgPiAwLjUpXQoKIyBDaGFuZ2UgY29sdW1ucyB0byBzbmFrZSBjYXNlCgpzbmFrZV9jYXNlIDwtIGZ1bmN0aW9uKHgpIHsKICBjb2xuYW1lcyh4KSA8LSBnc3ViKCIgIiwgIl8iLCBjb2xuYW1lcyh4KSk7eAogIGNvbG5hbWVzKHgpIDwtdG9sb3dlcihjb2xuYW1lcyh4KSkKfQoKY29sbmFtZXMoYnJ1dGFsaXR5X2NsZWFuKSA9IHNuYWtlX2Nhc2UoYnJ1dGFsaXR5X2NsZWFuKQoKYnJ1dGFsaXR5X2NsZWFuID0gYnJ1dGFsaXR5X2NsZWFuICU+JSAKICByZW5hbWUoCiAgICJzdGF0ZSIgPSBzdGF0ZV93aGVyZV90aGVfcG9saWNlX2JydXRhbGl0eV9vY2N1cnJlZAogICAsICJjaXR5IiA9IGNpdHlfd2hlcmVfdGhlX3BvbGljZV9icnV0YWxpdHlfb2NjdXJyZWQKICAgLCAiZGF0ZSIgPSBkYXRlX29mX3RoZV9wb2xpY2VfYnJ1dGFsaXR5CiAgKSAlPiUgCiAgbXV0YXRlKAogICAgc3RhdGVfY291bnR5ID0gcGFzdGUwKHN0YXRlLCAiLSIsIGNvdW50eSkKICApCgpicnV0YWxpdHlfY2xlYW4kZGF0ZSA9IGFzLkRhdGUoYnJ1dGFsaXR5X2NsZWFuJGRhdGUsICIlbS8lZC8lWSIpCgpgYGAKCmBgYHtyfQoKdXNfc3RhdHNfY2xlYW4gPSB1c19jb3VudHlfc3RhdHMKCnVzX3N0YXRzX2NsZWFuJGNvdW50eSA9IHN0cl9yZXBsYWNlKHVzX3N0YXRzX2NsZWFuJGNvdW50eSwgIiBDb3VudHkiLCAiIikKCnVzX3N0YXRzX2NsZWFuID0gdXNfc3RhdHNfY2xlYW4gJT4lIAogIGdyb3VwX2J5KHN0YXRlKSAlPiUgCiAgbXV0YXRlKHN0YXRlX3BvcCA9IHN1bShwb3B1bGF0aW9uKSkgJT4lIAogIHJlbmFtZSgKICAgICJzdGF0ZV9mdWxsIiA9IHN0YXRlCiAgICAsICJzdGF0ZSIgPSBzdGF0ZV9jb2RlCiAgICApICU+JSAKICB1bmdyb3VwKCkgCgojIHVzX3N0YXRzX2NsZWFuID0gCiAgCnVzX3N0YXRzX3N0YXRlID0gdXNfc3RhdHNfY2xlYW4gJT4lIAogIG11dGF0ZShzdGF0ZV9jb3VudHkgPSBwYXN0ZTAoc3RhdGUsICItIiwgY291bnR5KSkgJT4lIAogIHNlbGVjdChzdGF0ZV9wb3AsIHN0YXRlLCBzdGF0ZV9mdWxsKSAlPiUgCiAgdW5pcXVlKCkKCmBgYAoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQoKZGZfbWVyZ2VkID0gYnJ1dGFsaXR5X2NsZWFuICU+JSAKICBsZWZ0X2pvaW4odXNfc3RhdHNfc3RhdGUpICU+JSAKICBtdXRhdGUoc3RhdGVfcG9wID0gaWZlbHNlKHN0YXRlID09ICJEQyIsIDcwNTc0OSwgc3RhdGVfcG9wKQogICkgJT4lIAogIHVuaXF1ZSgpCgpkZl9tZXJnZWQgPSBkZl9tZXJnZWQgJT4lIAogIGdyb3VwX2J5KAogICAgc3RhdGUKICApICU+JSAKICBhZGRfdGFsbHkoKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICByZW5hbWUoCiAgICAic3RhdGVfdG90YWxzIiA9IG4KICApICU+JSAKICBtdXRhdGUoCiAgICBzdGF0ZV9wb3BfbWlsbGlvbnMgPSBzdGF0ZV9wb3AvMTAwMDAwMAogICAgLCB0b3RhbHNfcGVyX21pbGxpb24gPSBzdGF0ZV90b3RhbHMvc3RhdGVfcG9wX21pbGxpb25zCiAgKSAKCmBgYAoKIyBJbmNpZGVudHMgYnkgU3RhdGUKCmBgYHtyfQoKZGZfcGxvdCA9IGRmX21lcmdlZCAlPiUgCiAgc2VsZWN0KAogICAgc3RhdGUsIHN0YXRlX3BvcCwgc3RhdGVfZnVsbCwgc3RhdGVfcG9wX21pbGxpb25zLCBzdGF0ZV90b3RhbHMsIHRvdGFsc19wZXJfbWlsbGlvbgogICkgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWxzX3Blcl9taWxsaW9uKSkgJT4lIAogIG11dGF0ZSgKICAgIHN0YXRlID0gcmVvcmRlcihhcy5mYWN0b3Ioc3RhdGUpLCB0b3RhbHNfcGVyX21pbGxpb24pCiAgKSAKCmEgPSBkZl9wbG90ICU+JSAKICBwbG90X2x5KAogICAgeSA9IH5zdGF0ZQogICAgLCB4ID0gfnRvdGFsc19wZXJfbWlsbGlvbgogICAgLCB0eXBlID0gImJhciIKICAgICwgY29sb3IgPSBwYWlyZWRfYmV0dGVyWzFdCiAgICAsIG9yaWVudGF0aW9uID0gImgiCiAgICAsIHdpZHRoID0gOTAwCiAgICAsIGhlaWdodCA9IDEwMDAKICAgICwgaG92ZXJpbmZvID0gInRleHQiCiAgICAsIHRleHQgPSB+cGFzdGUoCiAgICAgICJTdGF0ZTogIiwgc3RhdGVfZnVsbAogICAgICAsICI8YnI+U3RhdGUgUG9wdWxhdGlvbjogIiwgY29tbWEoc3RhdGVfcG9wKQogICAgICAsICI8YnI+UmVwb3J0ZWQgSW5jaWRlbnRzOiAiLCBzdGF0ZV90b3RhbHMKICAgICAgLCAiPGJyPkluY2lkZW50cyBwZXIgTWlsbGlvbiBSZXNpZGVudHM6ICIsIGNvbW1hKHRvdGFsc19wZXJfbWlsbGlvbikKICAgICkKICApICU+JSAKICBsYXlvdXQoCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVwb3J0ZWQgSW5jaWRlbnRzIHBlciBNaWxsaW9uIFN0YXRlIFJlc2lkZW50cyIpCiAgICAsIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKQogICkKCmIgPSBkZl9wbG90ICU+JSAKICBwbG90X2x5KAogICAgeSA9IH5zdGF0ZQogICAgLCB4ID0gfnN0YXRlX3RvdGFscwogICAgLCB0eXBlID0gImJhciIKICAgICwgY29sb3IgPSBwYWlyZWRfYmV0dGVyWzEwXQogICAgLCBvcmllbnRhdGlvbiA9ICJoIgogICAgLCB3aWR0aCA9IDkwMAogICAgLCBoZWlnaHQgPSAxMDAwCiAgICAsIGhvdmVyaW5mbyA9ICJ0ZXh0IgogICAgLCBzaG93bGVnZW5kID0gRkFMU0UKICAgICwgdGV4dCA9IH5wYXN0ZSgKICAgICAgIlN0YXRlOiAiLCBzdGF0ZV9mdWxsCiAgICAgICwgIjxicj5TdGF0ZSBQb3B1bGF0aW9uOiAiLCBjb21tYShzdGF0ZV9wb3ApCiAgICAgICwgIjxicj5SZXBvcnRlZCBJbmNpZGVudHM6ICIsIHN0YXRlX3RvdGFscwogICAgICAsICI8YnI+SW5jaWRlbnRzIHBlciBNaWxsaW9uIFJlc2lkZW50czogIiwgY29tbWEodG90YWxzX3Blcl9taWxsaW9uKQogICAgKQogICkgJT4lIAogIGxheW91dCgKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJUb3RhbCBSZXBvcnRlZCBJbmNpZGVudHMiKQogICAgLCB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIikKICApCgpzdWJwbG90KGEsIGIsIHRpdGxlWCA9IFRSVUUpCgpgYGAKCiMjIyBfRXhjbHVkaW5nIGNvdW50aWVzIHdpdGggbnVsbCBwb3B1bGF0aW9uc18KVGhlcmUgYXJlIHNvbWUgY291bnRpZXMgd2l0aCBudWxsIHZhbHVlcyB3aGVuIGl0IGNvbWVzIHRvIHBvcHVsYXRpb24uIFdlIGNhbiBvYnZpb3VzbHkgZmluZCB0aGVzZSB2aWEgYSBiZXR0ZXIgZGF0YXNldCwgYnV0IGZvciBub3cgSSdtIGdvaW5nIHRvIGV4Y2x1ZGUgdGhlbSBpbiBvcmRlciB0byBwcm92aWRlIGV4YW1wbGVzIHdpdGggdGhlIGRhdGEgdGhhdCB3ZSBoYXZlLiBfT2J2aW91c2x5LCB0aGlzIGlzIG5vdCBhY2N1cmF0ZSByZXBvcnRpbmcuXwoKIyBDb3VudHkgSW5jaWRlbnRzIHBlciBDYXBpdGEgCgpgYGB7cn0KCmRmX2ZpbHRlcmVkID0gZGZfbWVyZ2VkCgpkZl9maWx0ZXJlZCA9IGRmX2ZpbHRlcmVkICU+JSAKICBsZWZ0X2pvaW4odXNfc3RhdHNfY2xlYW4pICU+JSAKICBmaWx0ZXIoIWlzLm5hKHBvcHVsYXRpb24pKSAKCmRmX2ZpbHRlcmVkID0gZGZfZmlsdGVyZWQgJT4lIAogIGdyb3VwX2J5KHN0YXRlLCBjb3VudHkpICU+JSAKICBhZGRfdGFsbHkoKSAlPiUgCiAgcmVuYW1lKCJjb3VudHlfdG90YWxzIiA9IG4pICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnR5X3RvdGFscykpICU+JSAKICBtdXRhdGUoImluY2lkZW50c19wZXJfY2FwaXRhIiA9IGNvdW50eV90b3RhbHMvKHBvcHVsYXRpb24pKSAlPiUgCiAgdW5ncm91cCgpIAoKZGZfY291bnR5ID0gZGZfZmlsdGVyZWQgJT4lIAogIHNlbGVjdChzdGF0ZSwgc3RhdGVfY291bnR5LCBjb3VudHksIHN0YXRlX2Z1bGwsIHN0YXRlX3RvdGFscywgbWVkaWFuX2FnZSwgcG9wdWxhdGlvbiwgY291bnR5X3RvdGFscywgaW5jaWRlbnRzX3Blcl9jYXBpdGEpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgYXJyYW5nZShkZXNjKGluY2lkZW50c19wZXJfY2FwaXRhKSkKCmRmX2NvdW50eSAlPiUgCiAgbXV0YXRlKAogICAgaW5jaWRlbnRzX3Blcl9jYXBpdGEgPSBjb21tYShpbmNpZGVudHNfcGVyX2NhcGl0YSwgLjAwMDAwMSkKICAgICwgcG9wdWxhdGlvbiA9IGNvbW1hKHBvcHVsYXRpb24pCiAgICApICU+JSAKICBkYXRhdGFibGUoKQoKCmBgYAoK